#include "widget.h"

#include <algorithm>

#include "gui/guimessage.h"

#include "matrixstack/matrixstack.h"

#include "renderer/renderer.h"

#include "resourcemanager.h"

Widget::Widget():Pickable(){
    x = 0;
    y = 0;
    z = 1;
    hidden = false;
    hideChildren = false;
    pickOffsetUniform = 0;
    drawOffsetUniform = 0;
    colorUniform = 0;
    obscurorUniform = 0;
    numVertices = 0;
    bgColor[0] = 1;
    bgColor[1] = 1;
    bgColor[2] = 0;
    enabled = true;
    selected = false;
    texture = -1;
    parent = nullptr;

	glGenVertexArrays(1, &drawVAO);
	glBindVertexArray(drawVAO);

	glGenBuffers(1, &vertexVBO);
	glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 4, GL_INT, GL_FALSE, 0, 0);

	glGenBuffers(1, &uvVBO);
	glBindBuffer(GL_ARRAY_BUFFER, uvVBO);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

	glBindVertexArray(0);


	glGenVertexArrays(1, &pick_vao);
	glBindVertexArray(pick_vao);

	glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 4, GL_INT, GL_FALSE, 0, 0);

	glBindVertexArray(0);
}

void Widget::SetHeight(int h){
    height = h;
    BuildVBO();
}
void Widget::SetWidth(int w){
    width = w;
    BuildVBO();
}

std::vector<Widget*> Widget::GetChildren(){
    std::vector<Widget*> raw_children;
    for(const auto & child:children){
        raw_children.push_back(child.get());
    }
    return raw_children;
}
// Draw ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Widget::Draw(){
    if(hidden){
        return;
    }
    if(texture == -1){
        texture = resourceManager->GetTexture("button");
    }

    PreDraw();
    if(numVertices > 0){
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture);
        glBindVertexArray(drawVAO);
        glDrawArrays(GL_QUADS, 0, numVertices);
        glBindVertexArray(0);
    }

    PostDraw();
}

void Widget::PreDraw(){
    guiStack->PushTransform();
    guiStack->Translate(x, y, z);

    if(drawOffsetUniform == 0){
        GLint program;
        glGetIntegerv(GL_CURRENT_PROGRAM, &program);
        drawOffsetUniform = glGetUniformLocation(program, "offset");
        if(drawOffsetUniform == -1){
            std::cout<<"Error locating offset uniform\n";
        }
    }

    if(obscurorUniform == 0){
        GLint program;
        glGetIntegerv(GL_CURRENT_PROGRAM, &program);
        obscurorUniform = glGetUniformLocation(program, "obscuror");
        if(obscurorUniform == -1){
            std::cout<<"Error locating obscuror uniform\n";
        }
    }

    glUniform4f(drawOffsetUniform, guiStack->Getx(), guiStack->Gety(), guiStack->Getz(), 0);
    glUniform1i(obscurorUniform, 0);
}

void Widget::PostDraw(){
//    glBindTexture(GL_TEXTURE_2D, 0);
    if(!hideChildren){
        for(const auto & child:children){
            child->Draw();
        }
    }
    guiStack->PopTransform();
}
// Pick ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Widget::Pick(){
    if(hidden){
        return;
    }
    PrePick();
    if(numVertices > 0){
        glBindVertexArray(pick_vao);
        glDrawArrays(GL_QUADS, 0, numVertices);
        glBindVertexArray(0);
    }
    PostPick();
}

void Widget::PrePick(){
    guiStack->PushTransform();
    guiStack->Translate(x, y, z);

    if(pickOffsetUniform == 0){
        GLint program;
        glGetIntegerv(GL_CURRENT_PROGRAM, &program);
        pickOffsetUniform = glGetUniformLocation(program, "offset");
        if(pickOffsetUniform == -1){
            std::cout<<"Error locating offset uniform\n";
        }
    }
    if(colorUniform == 0){
        GLint program;
        glGetIntegerv(GL_CURRENT_PROGRAM, &program);
        colorUniform = glGetUniformLocation(program, "color");
        if(colorUniform == -1){
            std::cout<<"Error locating color uniform\n";
        }
    }
    glUniform4f(pickOffsetUniform, guiStack->Getx(), guiStack->Gety(), guiStack->Getz(), 0);
    glUniform4f(colorUniform, fRed(), fGreen(), fBlue(), 1);
}

void Widget::PostPick(){
    if(!hideChildren){
        for(const auto & child:children){
            child->Pick();
        }
    }
    guiStack->PopTransform();
}


// VBO /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Widget::BuildVBO(){
    int vertices[] = { 0, -height, 0, 1,
                       0, 0, 0, 1,
                       width, 0, 0, 1,
                       width, -height, 0, 1};

    float uvs[] = { 0, 0,
                    0, 1,
                    1, 1,
                    1, 0};
    numVertices = 4;

    BuildVBO(vertices, numVertices, uvs);
}

void Widget::BuildVBO(int* vertices, int numVert, float* uvs){
	glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
	glBufferData(GL_ARRAY_BUFFER, numVert*4*sizeof(int), vertices, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindBuffer(GL_ARRAY_BUFFER, uvVBO);
    glBufferData(GL_ARRAY_BUFFER, numVert*2*sizeof(float), uvs, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void Widget::AddChild(std::unique_ptr<Widget> new_child){
    new_child->SetParent(this);
    children.push_back(std::move(new_child));
}


Widget* Widget::GetChild(std::string name){
    auto iter = std::find_if(children.begin(), children.end(), [name](const std::unique_ptr<Widget> & child){ return child->GetName() == name; });
    return iter == children.end() ? nullptr : iter->get();
}

GUIMessage* Widget::Click(int color){
    if(GetID()==color){
        std::cout<<name<<" was clicked\n";
        return clickedMessage.get();
    }
    else{
        for(const auto & child:children){
            GUIMessage* message = child->Click(color);
            if(message!=nullptr){
                return ProcessMessage(message);
            }
        }
        return nullptr;
    }
}

GUIMessage* Widget::ProcessMessage(GUIMessage* message){
    return message;
}

void Widget::ToggleShow(){
    if(hidden){
        Show();
    }
    else{
        Hide();
    }
}

void Widget::DestroyChildren(){
    children.clear();
}
